#include "dataprovider.h"
#include <QJsonArray>
#include <QSemaphore>
#include <QScopeGuard>
#include "record.h"

DataProvider::DataProvider(QObject *parent)
    : QObject{parent}
{
    if (!localDb.Open(DB_PATH)) {
        qDebug() << "打开数据库失败：" << DB_PATH;
    };

    QJsonObject data;
    data = localDb.ReadShopInfo();
    qDebug() << "读取的门店信息...";
    shop = ShopInfo::FromJson(data);
    selectedMaterialType = "全部";
    shortName = "";

    auto mac = netManager.getMacAddress();
    Record::SetLocalMac(mac);

    timer = new QTimer(this);

    timer->setInterval(60 * 1000); //定时与服务器同步数据；

    connect(&netManager, &NetworkManager::requestFailed, this, [=](const QString &error){
        emit proccessFailed(error);
    });

    connect(&netManager, &NetworkManager::tokenExpired, this, [=](const QString &error){
        emit tokenExpired(error);
    });

    connect(&printer, &Printer::printerFailed, this, [=](const QString &error){
        emit proccessFailed("打印问题：" + error);
    });

    connect(timer, &QTimer::timeout, this, [=]{
        UploadRecords();
        doWarning();
    });

    effect = new QSoundEffect(this);
    effect->setSource(QUrl::fromLocalFile(":/sound/ding.wav"));
    effect->setVolume(0.8f);  // 音量范围0~100
    effect->setLoopCount(3);
}

void DataProvider::SetShortName(QString c){
    shortName = c;
    emit materialListUpdate();
    emit recordListUpdate();
}

void DataProvider::doWarning() {
    for (auto record: records) {
        if (record->isExpiring() && !record->HadWarnning) {
            record->HadWarnning = true;
            effect->play();

            break;
        }
    }
}

bool DataProvider::UploadRecords(){
    auto records = localDb.ReadRecords(shop->ShopCode);
    if (records.size() > 0) {
        uploadRecords(records);
        return true;
    }
    return false;
}

QtPromise::QPromise<void> DataProvider::uploadChunk(const QJsonArray& chunk) {
    return QtPromise::QPromise<void>([=](auto resolve, auto reject) {
        QNetworkRequest request(QUrl("http://upload.server/chunk"));
        netManager.UploadRecords(chunk)
            .then([=]{
                resolve();
            })
            .fail([=](const NetErrorInfo& error){
                reject(error);
            });
        });
}

QtPromise::QPromise<void> DataProvider::uploadRecords(QJsonArray list) {
    QList<QtPromise::QPromise<void>> promises;
    const int chunkSize = 200;
    auto semaphore = QSharedPointer<QSemaphore>::create(3);

    // 修复1：手动分块实现
    for (int i = 0; i < list.size(); i += chunkSize) {
        const auto end = std::min(i + chunkSize, list.size());
        QJsonArray chunk;
        std::copy(list.begin() + i, list.begin() + end, std::back_inserter(chunk));

        promises.append(QtPromise::QPromise<void>(
            [=](const auto& resolve, const auto& reject) mutable {
                semaphore->acquire(1);
                auto guard = qScopeGuard([semaphore] { semaphore->release(1); });

                uploadChunk(chunk)
                    .then([=] {
                        QMutexLocker locker(&localDb.m_mutex);
                        localDb.RemoveRecords(chunk);
                        resolve();
                    })
                    .fail([=](const std::exception& e) {
                        qDebug() << "Chunk error:" << e.what();
                        // 修复2：传递异常对象
                        reject(std::current_exception());
                    })
                    // 修复3：移除finally中的guard移动
                    .finally([] {});
            }));
    }

    // 修复4：添加全局异常处理
    return QtPromise::all(promises).fail([](const std::exception& e) {
        qCritical() << "Global error:" << e.what();
        throw; // 继续向上传播
    });
}


void DataProvider::Login(QString tenant, QString username, QString password){
    netManager.Login(tenant, username, password)
        .then([=](QJsonObject data){
            shop = ShopInfo::FromJson(data);
            qDebug() << "download shopName: " << shop->ShopName;
            return netManager.QueryPrinterTemplate();
        })
        .then([=](QJsonObject data){
            data["tenantSign"] = shop->TenantSign;
            data["shopCode"] = shop->ShopCode;
            data["shopName"] = shop->ShopName;
            qDebug() << "save shopName: " << data["shopName"];
            localDb.SaveShopInfo(data);
            const QJsonObject s = localDb.ReadShopInfo();
            shop = ShopInfo::FromJson(s);
            qDebug() << "local Db shopName: " << shop->ShopName;
            printer.SetPrinterInfo(shop->PrinterDpi,
                                   shop->PrinterName,
                                   shop->PrinterIp,
                                   shop->PrinterPort,
                                   shop->HShift,
                                   shop->VShift,
                                   shop->PrinterXML,
                                   shop->PrinterTemplateVersion);
            return netManager.QueryMerterialList();
        })
        .then([=](QJsonObject data){
            if (data.contains("list")) {
                QJsonArray list = data["list"].toArray();
                QSet<QString> seen;
                QList<QString> rawTypes;
                for (const QJsonValueRef &value: list) {
                    if (value.isObject()) {
                        auto obj = value.toObject();
                        auto m = Material::FromJson(obj);
                        if (!seen.contains(m->TypeName)) {
                            seen.insert(m->TypeName);
                            rawTypes.append(m->TypeName);
                        }
                        materials.append(m);
                    }
                }
                rawTypes.sort();
                materialTypes.clear();
                materialTypes.append("全部");
                materialTypes.append(rawTypes);
            }
            timer->start(); //下载完模板开始计时上传数据。
            emit materialListUpdate();
            auto list = localDb.ReadRecords(shop->ShopCode);
            return uploadRecords(list);
        }).then([=]{
            return netManager.QueryMaterialRecordList();
        }).then([=](QJsonObject data){
            if (data.contains("list")) {
                QJsonArray list = data["list"].toArray();
                for (const QJsonValueRef &value: list) {
                    if (value.isObject()) {
                        auto obj = value.toObject();
                        auto r = Record::FromJson(obj);
                        records.append(r);
                    }
                }
            }
            emit proccessReady();
        }).fail([=](NetErrorInfo error){
            if (error.code == -20) {
                emit tokenExpired(error.message);
            } else {
                emit proccessFailed(error.message);
            }
        });
}

QList<Material*> DataProvider::GetMaterials(){
    if ((selectedMaterialType.isEmpty() || selectedMaterialType == "全部") && shortName == "") {
        return materials;
    }
    QList<Material*> result;
    std::copy_if(materials.begin(), materials.end(), std::back_inserter(result),[=](const Material* p){
        bool flag = true;
        if (!selectedMaterialType.isEmpty() && selectedMaterialType != "全部") {
            flag = p->TypeName == selectedMaterialType;
            if (flag == false) {
                return false;
            }
        }
        if (shortName != "") {
            flag &= p->ShortName.startsWith(shortName);
        }
        return  flag;
    });
    return result;
}

QList<Record*> DataProvider::GetRecords(){
    QList<Record*> result;
    if ((selectedMaterialType.isEmpty() || selectedMaterialType == "全部") && shortName == "") {
        result = records;
    } else {
        std::copy_if(records.begin(), records.end(), std::back_inserter(result),[=](const Record* p){
            bool flag = true;
            if (!selectedMaterialType.isEmpty() && selectedMaterialType != "全部") {
                flag = p->TypeName == selectedMaterialType;
                if (flag == false) {
                    return false;
                }
            }
            if (shortName != "") {
                flag &= p->ShortName.startsWith(shortName);
            }
            return  flag;
        });
    }

    std::sort(result.begin(), result.end(), [](const Record* a, const Record* b){
        //使用 <= 判断会造成内存越界
        return a->VailAt < b->VailAt;
    });

    return result;
}

void DataProvider::SetSelectedMaterialType(const QString &t){
    selectedMaterialType = t;
    // qDebug() << "选中类别：" << t;
    emit materialListUpdate();
    emit recordListUpdate();
}

void DataProvider::SetSelectedRecord(Record* r){
    selectedRecord = r;
    // qDebug() << "选中Record：" << r->Name;
    emit recordUpdate(r);
};

Material* DataProvider::getMaterialById(int id){
    auto m = std::find_if(materials.begin(), materials.end(), [=](const Material* p){
        return p->Id == id;
    });
    if (m != materials.end()) {
        return *m;
    }
    return nullptr;
}

void DataProvider::PrintRecord(Record& r){
    printer.PrintRecord(r);
}

void DataProvider::CreateRecordByMaterial(int MaterialId){
    auto m = getMaterialById(MaterialId);
    if (m != nullptr) {
        auto r = Record::FromMaterial(m, shop->ShopCode);
        records.append(r);
        // qDebug() << "添加记录：" << r->LocalId;
        QJsonArray list;
        list.append(r->ToJson());
        localDb.SaveRecords(list);
        PrintRecord(*r);
        emit recordListUpdate();
    }
}

void DataProvider::SplitRecord(Record* r){
    auto newRecord = Record::FromParent(r, shop->ShopCode);
    if (newRecord) {
        records.append(newRecord);
        QJsonArray list;
        list.append(newRecord->ToJson());
        localDb.SaveRecords(list);
        PrintRecord(*newRecord);
        emit recordListUpdate();
    }
}

void DataProvider::DeleteRecord(QString id) {
    auto r = std::find_if(records.begin(), records.end(), [=](const Record* p) {
        return p->LocalId == id;
    });
    if (r != records.end()) {
        records.removeOne(*r);
        (*r)->Delete();
        QJsonArray list;
        list.append((*r)->ToJson());
        localDb.SaveRecords(list);
        emit recordListUpdate();
    }
}

void DataProvider::ChangePassword(QString oldPassword, QString newPassword){
    netManager.ChangePassword(oldPassword, newPassword);
}

void DataProvider::DeleteAllExpiredRecords(){
    QJsonArray list;
    for (auto record: records) {
        if (record->isExpired()) {
            records.removeOne(record);
            record->Delete();
            list.append(record->ToJson());
        }
    }
    localDb.SaveRecords(list);
    emit recordListUpdate();
}

DataProvider::~DataProvider() {
    delete shop;
}
